package pl.poznan.put.cs.to.tsp.listeners;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.util.LinkedList;

import javax.swing.JFrame;
import javax.swing.JPanel;

import pl.poznan.put.cs.to.tsp.common.Vertexes;

@SuppressWarnings("serial")
public class GraphicWindow extends JFrame {
	private static final int BASE_WIDTH = 900;
	private static final int BASE_HEIGHT = 700;
	private static final int ADDITIONAL_SIZE = 40;
	private int minX;
	private int maxX;
	private int minY;
	private int maxY;
	private int count;
	private JPanel mainPanel;
	private Image image = new BufferedImage(GraphicWindow.BASE_WIDTH + 2
			* GraphicWindow.ADDITIONAL_SIZE, GraphicWindow.BASE_HEIGHT + 2
			* GraphicWindow.ADDITIONAL_SIZE, BufferedImage.TYPE_INT_ARGB);

	public GraphicWindow() {
		this.initGUI();
	}

	private void findMinMax(Vertexes vertexes) {
		this.minX = Integer.MAX_VALUE;
		this.maxX = Integer.MIN_VALUE;
		this.minY = Integer.MAX_VALUE;
		this.maxY = Integer.MIN_VALUE;
		this.count = vertexes.getNumberOfNodes();
		for (int i = 0; i < this.count; i++) {
			if (vertexes.getX(i) < this.minX) {
				this.minX = vertexes.getX(i);
			}
			if (vertexes.getX(i) > this.maxX) {
				this.maxX = vertexes.getX(i);
			}
			if (vertexes.getY(i) < this.minY) {
				this.minY = vertexes.getY(i);
			}
			if (vertexes.getY(i) > this.maxY) {
				this.maxY = vertexes.getY(i);
			}
		}
	}

	synchronized public void paintSolution(LinkedList<Integer> listA,
			LinkedList<Integer> listB, Vertexes vertexes) {
		this.findMinMax(vertexes);
		this.image = new BufferedImage(GraphicWindow.BASE_WIDTH + 2
				* GraphicWindow.ADDITIONAL_SIZE, GraphicWindow.BASE_HEIGHT + 2
				* GraphicWindow.ADDITIONAL_SIZE, BufferedImage.TYPE_INT_ARGB);
		Graphics g = this.image.getGraphics();
		g.setColor(Color.WHITE);
		g.fillRect(0, 0, GraphicWindow.BASE_WIDTH + 2
				* GraphicWindow.ADDITIONAL_SIZE, GraphicWindow.BASE_HEIGHT + 2
				* GraphicWindow.ADDITIONAL_SIZE);
		g.setColor(Color.RED);
		drawList(Color.RED, listA, vertexes, g);
		drawList(Color.BLUE, listB, vertexes, g);
		this.repaint();
	}

	private void drawList(Color color, LinkedList<Integer> list,
			Vertexes vertexes, Graphics g) {
		int ax, ay, bx, by;
		int previous;
		g.setColor(color);
		previous = -1;
		for (int i : list) {
			if (previous == -1) {
				ax = vertexes.getX(list.getLast());
				ay = vertexes.getY(list.getLast());
				bx = vertexes.getX(i);
				by = vertexes.getY(i);
			} else {
				ax = vertexes.getX(previous);
				ay = vertexes.getY(previous);
				bx = vertexes.getX(i);
				by = vertexes.getY(i);
			}
			previous = i;
			this.drawEdge(ax, ay, bx, by, g);
		}
	}

	private void initGUI() {
		this.setPreferredSize(new Dimension(GraphicWindow.BASE_WIDTH + 2
				* GraphicWindow.ADDITIONAL_SIZE, GraphicWindow.BASE_HEIGHT + 2
				* GraphicWindow.ADDITIONAL_SIZE));
		this.setMaximumSize(this.getPreferredSize());
		this.setMinimumSize(this.getPreferredSize());
		this.setResizable(false);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setLayout(new BorderLayout());

		this.mainPanel = new JPanel() {
			@Override
			public void paint(Graphics g) {
				super.paint(g);
				g.setColor(Color.BLACK);

				g.drawImage(image, 0, 0, null);
			}
		};

		this.getContentPane().add(this.mainPanel, BorderLayout.CENTER);
		this.setVisible(true);
	}

	private void drawEdge(int ax, int ay, int bx, int by, Graphics g) {
		int scaledX1 = GraphicWindow.ADDITIONAL_SIZE
				+ ((ax - this.minX) * GraphicWindow.BASE_WIDTH / (this.maxX - this.minX));

		int scaledY1 = GraphicWindow.ADDITIONAL_SIZE
				+ ((ay - this.minY) * GraphicWindow.BASE_HEIGHT / (this.maxY - this.minY));

		int scaledX2 = GraphicWindow.ADDITIONAL_SIZE
				+ ((bx - this.minX) * GraphicWindow.BASE_WIDTH / (this.maxX - this.minX));

		int scaledY2 = GraphicWindow.ADDITIONAL_SIZE
				+ ((by - this.minY) * GraphicWindow.BASE_HEIGHT / (this.maxY - this.minY));

		g.drawLine(scaledX1, scaledY1, scaledX2, scaledY2);
	}
}
